// import React, { useRef } from 'react';
import { v1 as uuidv1 } from 'uuid';
import { jsapi, viewUtils } from '@/utils/arcgis';
import {
  graphicFromEvent,
  updateGraphicFromGeometry,
  geometryFromSketch,
  createGraphic,
} from './helper';

let curTask = null; // 用于判断当前是否有绘制任务执行
let activeHandle = null; // 当前绘制的promise对象（更底层）
const taskManage = {}; // 用于管理取消绘制的操作，cancel中来reject promise （封装之后）
// const graphicManage = {};

// 用于在外部回调中获取当前任务状态
export function getCurTask() {
  return curTask;
}

/**
 * 绘制和更新点
 * @param {*} ctx  {graphic, view, layer}
 * @param {*} isCreate 默认true 创建或是更新
 * @returns
 */
export function drawPoint(ctx, isCreate = true) {
  const { graphic, view, layer } = ctx;
  ctx.geometryType = 'point';

  const result = runSketchViewModel(ctx, isCreate); // 直接调用 DrawGeometry 实例化 SketchViewMode
  // Update graphic when mouse moves
  const handler = view.on('pointer-move', event => {
    // Converts the given screen point to a map point.
    const mapPoint = view.toMap(event, {
      include: view.map.allLayers.filter(l => l !== layer),
    });
    if (mapPoint) {
      // const snappedPoint = this.snapAndAddZ(mapPoint);
      updateGraphicFromGeometry(mapPoint, graphic, layer);
    }
  });

  // Remove event listener when operation is done
  return result.finally(() => {
    handler.remove();
    ctx = null;
  });
}

/**
 * 绘制和更新线
 * @param {*} ctx {graphic, view, layer}
 * @param {*} isCreate  默认true 创建或是更新
 * @param {*} sketchColor  绘制颜色
 * @returns
 */
export async function drawPolyline(ctx, isCreate = true, sketchColor) {
  ctx.geometryType = 'polyline';
  ctx.sketchColor = sketchColor;

  const [Color] = await jsapi.load(['esri/Color']);
  const color = new Color(sketchColor);
  color.a = 0.5;

  function afterSketchVmCreated(sketchVm) {
    sketchVm.polylineSymbol.color = color;
    sketchVm.pointSymbol.color = color;
    sketchVm.pointSymbol.outline.width = 0;
    ctx.sketchViewModel = sketchVm;
    return sketchVm;
  }

  const result = runSketchViewModel(ctx, isCreate, afterSketchVmCreated);
  return result.finally(() => {
    console.log('>----- runSketchViewModel finally');
    ctx = null;
    return 'finished';
  });
}

/**
 * 绘制和更新面
 * @param {*} ctx {graphic, view, layer}
 * @param {*} isCreate  默认true 创建或是更新
 * @param {*} sketchColor  绘制颜色
 * @returns
 */
export async function drawPolygon(ctx, isCreate = true, sketchColor) {
  ctx.geometryType = 'polygon';
  ctx.sketchColor = sketchColor;
  const [Color] = await jsapi.load(['esri/Color']);
  const color = new Color(sketchColor);
  color.a = 0.5;

  function afterSketchVmCreated(sketchVm) {
    sketchVm.polygonSymbol.color = color;
    sketchVm.polygonSymbol.outline.width = 0;

    sketchVm.polylineSymbol.color = color;

    sketchVm.pointSymbol.color = color;
    sketchVm.pointSymbol.outline.width = 0;

    ctx.sketchViewModel = sketchVm;

    return sketchVm;
  }

  const result = runSketchViewModel(ctx, isCreate, afterSketchVmCreated);
  return result.finally(() => {
    ctx = null;
  });
}

// ==========================================================
// DrawGeometry
// ==========================================================

/**
 * 创建sketchViewModel 和 生成promise流
 * @param {boolean} isCreate
 * @returns promise
 */
async function runSketchViewModel(ctx, isCreate, afterSketchVmCreated) {
  const { view, layer, graphic } = ctx;
  let taskId = uuidv1();
  if (activeHandle) {
    // 结束上一个promises
    // If a draw operation is already active, we cancel it and ignore the new one.
    activeHandle.reject();
    return Promise.reject();
  }

  // 获取高亮样式配置
  const haloOpacity = view.highlightOptions.haloOpacity;
  const fillOpacity = view.highlightOptions.fillOpacity;

  let sketchViewModel = await createSketchViewModel(view, layer);
  if (afterSketchVmCreated) {
    // sketchViewModel =
    sketchViewModel = afterSketchVmCreated(sketchViewModel);
  }
  const keyEventListener = view.on('key-down', event => {
    const remove = event.key === 'Delete' || event.key === 'Backspace';
    if (remove || event.key === 'Escape') {
      if (remove || isCreate) {
        layer.remove(graphic); // 以上按键按下是触发删除,闭包
      }

      taskManage[taskId].cancel();
    }
  });

  const promise = initiateTask(
    handle => {
      // 继承自WidgetOperation的函数
      activeHandle = handle; // resolve 和 reject
      if (isCreate) {
        view.highlightOptions.haloOpacity = 0;
      }
      view.highlightOptions.fillOpacity = 0;

      // 增加事件监听回调，真正的开始绘制
      sketchViewModel.on('create', event => {
        onSketchViewModelEvent(sketchViewModel, event, event.state === 'cancel', handle, ctx);
      });

      sketchViewModel.on('update', event => {
        onSketchViewModelEvent(sketchViewModel, event, event.aborted, handle, ctx);
      });

      launchSketchViewModel(sketchViewModel, isCreate, ctx);
      view.focus();
    },
    () => {
      // 全局current handle
      sketchViewModel.cancel();
      taskManage[taskId] = null;
      curTask = null;
      console.log('cancel');
    },
    cancel => {
      taskManage[taskId] = { cancel, ctx, taskId };
      curTask = taskId;
    },
    () => {
      taskManage[taskId] = null;
      curTask = null;
    },
  );

  // Clean up
  return promise.finally(() => {
    activeHandle = null;
    console.log('clear sktech view mode');
    // Cleanup resources
    keyEventListener.remove();
    sketchViewModel.cancel();
    sketchViewModel.destroy();

    // Reset scene
    view.highlightOptions.haloOpacity = haloOpacity;
    view.highlightOptions.fillOpacity = fillOpacity;
  });
}

/**
 * 创建sketchViewModel
 * @param {*} view
 * @param {*} layer
 * @returns
 */
async function createSketchViewModel(view, layer) {
  const [SketchViewModel] = await jsapi.load(['esri/widgets/Sketch/SketchViewModel']);
  const svm = new SketchViewModel({
    view, // 需要scene
    layer, // 需要layer
    updateOnGraphicClick: false,
  });

  svm.defaultCreateOptions.hasZ = false;
  return svm;
}

// ! 此代码只在4.18后才能正常运行，4.18前poly在绘制过程中的事件中，没有graphic
/**
 * 在sketchViewModel上绑定事件，用于在绘制过程中和结束后对graphic进行操作
 * @param {*} sketchViewModel
 * @param {*} event
 * @param {*} cancel
 * @param {*} handle
 * @param {*} ctx
 */
function onSketchViewModelEvent(sketchViewModel, event, cancel, handle, ctx) {
  const sketchGraphic = graphicFromEvent(event);
  console.log('onSketchViewModelEvent', event);
  // If we are done, remove extra sketch graphic
  if (event.state === 'complete' || cancel) {
    if (sketchGraphic && sketchGraphic !== ctx.graphic) {
      sketchViewModel.layer.remove(sketchGraphic);
    }
  }

  if (cancel || sketchGraphic === null) {
    //什么都没画出来 和 svm取消事件
    if (event.type === 'create') {
      console.log('------ end ---------');
      ctx.layer.remove(ctx.graphic);
    }
    handle.reject(); // 触发包裹的promise流的 reject分支
  } else {
    const geometry = geometryFromSketch(sketchGraphic);
    updateGraphicFromGeometry(geometry, ctx.graphic, ctx.layer);

    if (event.state === 'complete') {
      if (geometry) {
        handle.resolve(geometry);
      } else {
        ctx.layer.remove(ctx.graphic);
        handle.reject();
      }
    }
  }
}

/**
 * 执行sketchViewModel 的创建或者更新 方法
 * @param {*} sketchViewModel
 * @param {*} create
 * @param {*} ctx
 */
function launchSketchViewModel(sketchViewModel, create, ctx) {
  const sketchGraphic = createGraphic(ctx);
  if (create) {
    sketchViewModel.create(ctx.geometryType);
  } else {
    // Remove z value for point graphics as currently the SketchViewModel won't allow that
    const hasZ = sketchGraphic.geometry.hasZ;
    const lastGeometry = sketchGraphic.geometry.clone();
    if (hasZ) {
      sketchGraphic.geometry.hasZ = false;
    }

    if (sketchGraphic.geometry.type === 'point') {
      sketchViewModel.update(sketchGraphic);
    } else {
      sketchViewModel.update(sketchGraphic, { tool: 'reshape' }); // 此处sketchVm接管编辑
    }

    if (hasZ) {
      sketchGraphic.geometry = lastGeometry;
    }
  }
}

// ==========================================================
// WidgetOperation
// ==========================================================

/**
 * 生成绘制promise
 * @param {fucntion} start 绑定sketchVm的监听事件
 * @param {fucntion} cancel
 * @param {fucntion} setGlobalRef
 * @param {fucntion} resetGlobalRef
 * @returns
 */
function initiateTask(start, cancel, setGlobalRef, resetGlobalRef) {
  const promise = new Promise((resolve, reject) => {
    setGlobalRef(cancel);
    start({ resolve, reject });
  });

  return promise.finally(() => {
    resetGlobalRef();
  });
}
